#pragma once

#include <vector>
#include <iostream>
#include <string>

namespace my
{
	// iint == Effective_big_int_integer
	class iint
	{
	private:

		typedef int unit;

		enum unit_sign
		{
			positive = '+',
			negative = '-'
		};
											//2147483647
		static const unit upLimit			= 1000000000;
		static const signed char unitDigit	= 9;

		std::vector<unit> _nums;
		unit_sign _sign = positive;

	private:

		void signChange() const
		{
			int* change = (int*)(&_sign);
			*change = (_sign == positive ? negative : positive);
		}

		static iint _pow(const iint& number, long long index);

		unit justThanSize(const iint& number) const;

		static unit_sign signExpressSolve(const std::string& str);

		static size_t signLastIndex(const std::string& str);

		static unit atoi(const std::string& number, int left, int right)
		{
			unit sum = 0;
			for (int i = left; i < right; ++i)
			{
				sum = sum * 10 + (number[i] - '0');
			}
			return sum;
		}

		static size_t digitCountUnit(unit n)
		{
			size_t count = 0;
			for (size_t temp = 1; temp <= n; temp *= 10)
			{
				++count;
			}
			return count;
		}

		size_t _getHead9() const
		{
			if (_nums.size() == 1)
			{
				return _nums[0];
			}

			unit HeadNum = _nums[_nums.size() - 1];
			unit temp = _nums[_nums.size() - 2];
			int rest = unitDigit - digitCountUnit(HeadNum);
			size_t setDigit = upLimit / 10;

			while (rest)
			{
				HeadNum = HeadNum * 10 + temp / setDigit % 10;
				setDigit /= 10;
				--rest;
			}
			return HeadNum;
		}

		bool _isJustHead9() const
		{
			if (_nums.size() == 1)
			{
				return true;
			}

			unit Head = _getHead9();
			*((iint*)this) -= 1;

			bool just = !(Head == _getHead9());

			*((iint*)this) += 1;
			return just;
		}

		void _downTen(long long n)
		{
			_nums.erase(_nums.begin(), _nums.begin() + n / unitDigit);
			*this /= _pow(10, n % unitDigit);
		}

		void _upTen(long long n)
		{
			_nums.insert(_nums.begin(), n / unitDigit, 0);
			*this *= _pow(10, n % unitDigit);
		}

	public:

		std::string to_string() const;

		size_t digitCount() const
		{
			return digitCountUnit(_nums[_nums.size() - 1]) + (_nums.size() - 1) * unitDigit;
		}

		void downTen(long long n)
		{
			if (n <= 0)
			{
				return;
			}
			_downTen(n);
		}

		void upTen(long long n)
		{
			if (n <= 0)
			{
				return;
			}
			_upTen(n);
		}

		static iint pow(const iint& number, long long index)
		{
			if (index == 0)
			{
				return 1;
			}
			if (number == 0 || index < 0)
			{
				return 0;
			}

			return _pow(number, index);
		}

	public:

		void swap(iint& number)
		{
			std::swap(_nums, number._nums);
			std::swap(_sign, number._sign);
		}

		iint(iint&& number)					= default;
		iint(const iint& number)			= default;
		iint& operator=(const iint& number) = default;
		iint& operator=(iint&& number)		= default;

		iint(const char* number)
		{
			*this = std::string(number);
		}

		iint(const std::string& number)
		{
			_sign = signExpressSolve(number);
			int downLimit = signLastIndex(number);

			_nums.reserve(number.size() / unitDigit + 1);

			for (int len = number.size(); len > downLimit + 1; len -= unitDigit)
			{
				_nums.push_back(atoi(number, (len - unitDigit >= 0 ? len - unitDigit : 0), len));
			}

			while (_nums.size() > 1 && _nums[_nums.size() - 1] == 0)
			{
				_nums.pop_back();
			}
		}

		iint(const std::vector<unit>&& number)
		{
			_nums = std::move(number);
		}
		iint(unsigned long long number)
			:_sign(positive)
		{
			_nums.push_back(number % upLimit);
			if (number >= upLimit)
			{
				number /= upLimit;
				_nums.push_back(number % upLimit);
				number /= upLimit;
				if (number != 0)
				{
					_nums.push_back(number % upLimit);
				}
			}
		}
		iint(const long long number = 0)
			:_sign(number < 0 ? negative : positive)
		{
			long long temp = number < 0 ? -number : number;
			unit prevHead = temp / upLimit / upLimit;
			unit prevNext = temp / upLimit % upLimit;
			_nums.push_back(temp % upLimit);
			if (prevHead > 0)
			{
				_nums.push_back(prevNext);
				_nums.push_back(prevHead);
			}
			else if (prevNext > 0)
			{
				_nums.push_back(prevNext);
			}
		}
		iint(const unsigned int number)
		{
			*this = iint((long long)number);
		}
		iint(const int number)
		{
			*this = iint((long long)number);
		}
		iint(const unsigned short number)
		{
			*this = iint((long long)number);
		}
		iint(const short number)
		{
			*this = iint((long long)number);
		}
		iint(const unsigned char number)
		{
			*this = iint((long long)number);
		}
		iint(const signed char number)
		{
			*this = iint((long long)number);
		}

	public:

		iint& operator+=(const iint& number);
		iint& operator-=(const iint& number);
		iint& operator*=(const iint& number);
		iint& operator/=(const iint& number);
		iint& operator%=(const iint& number);
		iint& operator<<=(const long long n);
		iint& operator>>=(const long long n);

	public:

		iint operator-() const
		{
			iint temp = *this;
			temp.signChange();
			return temp;
		}
		iint& operator--()
		{
			return *this -= 1;
		}
		iint operator--(int)
		{
			iint temp = *this;
			--(*this);
			return temp;
		}

		iint operator+() const
		{
			return *this;
		}
		iint& operator++()
		{
			return *this += 1;
		}
		iint operator++(int)
		{
			iint temp = *this;
			++(*this);
			return temp;
		}

	public:

		friend bool operator==(const iint& number1, const iint& number2);
		friend bool operator< (const iint& number1, const iint& number2);

		friend std::ostream& operator<<(std::ostream& out, const iint& number);
		friend std::istream& operator>>(std::istream& in, iint& number);
	};

	iint operator+(const iint& number1, const iint& number2);
	iint operator-(const iint& number1, const iint& number2);
	iint operator*(const iint& number1, const iint& number2);
	iint operator/(const iint& number1, const iint& number2);
	iint operator%(const iint& number1, const iint& number2);
	iint operator<<(const iint& number1, const long long n);
	iint operator>>(const iint& number1, const long long n);

	bool operator<=(const iint& number1, const iint& number2);
	bool operator>(const iint& number1, const iint& number2);
	bool operator>=(const iint& number1, const iint& number2);
	bool operator!=(const iint& number1, const iint& number2);
	bool operator!(const iint& number);
}
